package drds.plus.executor.record_codec.record;

import drds.plus.sql_process.abstract_syntax_tree.configuration.ColumnMetaData;
import drds.plus.sql_process.type.Type;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.Map.Entry;

@Slf4j
public class FixedLengthRecord extends Record {
    private final List<ColumnMetaData> columnMetaDataList;
    protected Object[] values;
    protected Map<String, Integer> columnNameToIndexMap;

    public FixedLengthRecord(Map<String, Integer> columnNameToIndexMap, List<ColumnMetaData> columnMetaDataList) {

        if (columnMetaDataList == null) {
            throw new NullPointerException("columnMetaDataList miss");
        }
        this.columnNameToIndexMap = columnNameToIndexMap;
        this.values = new Object[columnNameToIndexMap.size()];
        this.columnMetaDataList = columnMetaDataList;

    }

    public FixedLengthRecord(List<ColumnMetaData> columnMetaDataList) {

        if (columnMetaDataList == null) {
            throw new NullPointerException("columnMetaDataList miss");
        }

        this.columnNameToIndexMap = new HashMap(columnMetaDataList.size());
        for (ColumnMetaData key : columnMetaDataList) {
            if (!columnNameToIndexMap.containsKey(key.getColumnName())) {
                columnNameToIndexMap.put(key.getColumnName(), columnNameToIndexMap.size());
            }
        }
        this.values = new Object[columnNameToIndexMap.size()];
        this.columnMetaDataList = columnMetaDataList;

    }


    public void put(String key, Object value) {
        Integer index = columnNameToIndexMap.get(key);
        if (index == null) {
            throw new IllegalArgumentException("can't find columnName :" + key + " . currentJoinRowData indexes is " + columnNameToIndexMap);
        }
        values[index] = value;

    }

    public Object get(String key) {
        Integer index = columnNameToIndexMap.get(key);
        if (index == null) {
            if (log.isDebugEnabled()) {
                log.debug("can't find columnName :" + key + " .indexMapping comparativeList : " + columnNameToIndexMap);
            }
            return null;
        }
        return values[index];
    }

    public void putAll(Map<String, Object> map) {
        for (Entry<String, Object> e : map.entrySet()) {
            put(e.getKey(), e.getValue());
        }

    }

    public Object getValue(int index) {

        return values[index];
    }

    public void setValue(int index, Object value) {
        values[index] = value;

    }

    public void addAll(List<Object> valueList) {
        valueList.addAll(valueList);

    }

    public Map<String, Integer> getColumnNameToIndexMap() {
        return columnNameToIndexMap;
    }

    public List<String> getColumnNameList() {
        List<String> columnNameList = new ArrayList(columnNameToIndexMap.size());
        columnNameList.addAll(columnNameToIndexMap.keySet());
        return columnNameList;
    }

    public List<Object> getValueList() {
        return Arrays.asList(values);
    }

    public Map<String, Object> getColumnNameToValueMap() {
        Map<String, Object> map = new HashMap(values.length);
        for (Entry<String, Integer> entry : columnNameToIndexMap.entrySet()) {
            map.put(entry.getKey(), values[entry.getValue()]);
        }
        return map;
    }

    public int compareTo(IRecord o) {
        List<Object> values1 = null;
        if (o instanceof NamedRecord) {
            values1 = ((NamedRecord) o).getRecord().getValueList();
        } else {
            values1 = o.getValueList();
        }
        for (int k = 0; k < values1.size(); k++) {
            Object v1 = values1.get(k);
            Object v = values[k];
            int r = 0;
            if (v instanceof byte[]) {
                byte[] a = (byte[]) v;
                byte[] b = (byte[]) v1;
                int len = Math.min(a.length, b.length);
                for (int i = 0; i < len; i++) {
                    byte ai = a[i];
                    byte bi = b[i];
                    if (ai != bi) {
                        if (ai - bi != 0) {
                            r = ai - bi;
                            break;
                        }
                    }
                }
                r = a.length - b.length;
            } else {
                if (v != null && v1 != null) {
                    r = ((Comparable) v).compareTo(v1);
                } else if (v == null && v1 == null) {
                    r = 0;
                } else if (v == null) {
                    r = -1;
                } else if (v1 == null) {
                    r = 1;
                }
            }
            if (r != 0) {
                return r;
            }
        }
        return 0;
    }

    public Object clone() {
        FixedLengthRecord fixedLengthRecord = (FixedLengthRecord) super.clone();
        if (fixedLengthRecord.values != null) {
            fixedLengthRecord.values = Arrays.copyOf(fixedLengthRecord.values, fixedLengthRecord.values.length);
        }
        return fixedLengthRecord;
    }

    public String toString() {
        return "FixedLengthRecord{" + " columnValueList=" + Arrays.asList(values) + ", indexMapping=" + columnNameToIndexMap + '}';
    }

    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        final FixedLengthRecord other;
        if (obj instanceof FixedLengthRecord) {
            other = (FixedLengthRecord) obj;
        } else if (obj instanceof NamedRecord) {
            NamedRecord namedRecord = (NamedRecord) obj;
            if (namedRecord.record != null && namedRecord.record instanceof FixedLengthRecord) {
                other = (FixedLengthRecord) namedRecord.record;
            } else {
                return false;
            }
        } else {
            return false;
        }

        if (!Arrays.deepEquals(this.values, other.values)) {
            return false;
        }
        return this.columnNameToIndexMap == other.columnNameToIndexMap || (this.columnNameToIndexMap != null && this.columnNameToIndexMap.equals(other.columnNameToIndexMap));
    }

    public int hashCode() {
        int hash = 7;
        hash = 29 * hash + Arrays.deepHashCode(this.values);
        hash = 29 * hash + (this.columnNameToIndexMap != null ? this.columnNameToIndexMap.hashCode() : 0);
        return hash;
    }

    public Type getValueType(int index) {

        return columnMetaDataList.get(index).getType();
    }

    public Type getValueType(String columnName) {
        Integer indexNum = columnNameToIndexMap.get(columnName);
        if (indexNum == null) {
            throw new IllegalArgumentException("can't find columnName :" + columnName + " . currentJoinRowData indexes is " + columnNameToIndexMap);
        }
        return columnMetaDataList.get(indexNum).getType();
    }

}
